package com.ham.mud.state;

import com.ham.mud.Colors;
import com.ham.mud.ServerConnection;
import com.ham.mud.Targetable;
import com.ham.mud.TargetableService;
import com.ham.mud.characters.MudCharacter;
import com.ham.mud.characters.Vitals;
import com.ham.mud.characters.mobs.Mob;
import com.ham.mud.characters.player.Player;
import com.ham.mud.characters.player.PlayerService;
import com.ham.mud.commands.PlayerCommand;
import com.ham.mud.commands.RecallCommand;
import com.ham.mud.commands.skills.CriticalStrikeSkill;
import com.ham.mud.commands.skills.FleeSkill;
import com.ham.mud.commands.skills.ParrySkill;
import com.ham.mud.commands.skills.Skill;
import com.ham.mud.commands.spells.SpellCommand;
import com.ham.mud.items.Item;
import com.ham.mud.quests.KillQuest;
import com.ham.mud.quests.Quest;
import com.ham.mud.rooms.Room;
import com.ham.mud.rooms.RoomService;

import java.util.*;

/**
 * Created by hlucas on Jun 27, 2011 at 1:37:01 PM
 */
public class CombatState extends ConnectionState {
    boolean everyOther = false;


    private static final Random rand = new Random();
    public static final Map<Integer, String> combatScales = new HashMap<Integer, String>();
    static {
        combatScales.put(10, "looks fine.               ");
        combatScales.put(9,  "is scratched a bit.       ");
        combatScales.put(8,  "has some bruises.         ");
        combatScales.put(7,  "is starting to look hurt. ");
        combatScales.put(6,  "needs medical attention.  ");
        combatScales.put(5,  "looks hurt.               ");
        combatScales.put(4,  "is critically injured.    ");
        combatScales.put(3,  "has grievous injuries.    ");
        combatScales.put(2,  "is barely breathing.      ");
        combatScales.put(1,  "is holding on to life.    ");
        combatScales.put(0,  "is dying.                 ");
        combatScales.put(-1, "is gurgling blood.        ");
    }
    List<String> inputBuffer = new ArrayList<String>();
    MudCharacter combatWith;
    int delay = 0;

    public CombatState(ServerConnection connection, MudCharacter combatWith) {
        super(connection);
        this.combatWith = combatWith;
    }

    public MudCharacter getCombatWith() {
        return combatWith;
    }

    @Override
    public void processInput(String line) {
        if(line == null || line.trim().equals("")) {
            connection.println();
            connection.showPrompt();
            return;
        }
        
        inputBuffer.add(line);
    }

    private static void whatThe(ServerConnection connection, String line) {
        connection.printSolo("You can not do that during combat.");
    }

    @Override
    public void tick() {
        everyOther = !everyOther;
        if(everyOther) return;

        if(delay == 0) {
            PlayerCommand command = processInputBuffer(connection, inputBuffer);
            if(command != null) {
                connection.println();
                command.execute(connection);
                if(command instanceof FleeSkill) {
                    return;
                }
                if(command instanceof Skill) {
                    delay += ((Skill)command).getRounds();
                }
            } else {
                connection.println(Colors.NEW_LINE);
            }
        } else {
            delay--;
            connection.println(Colors.NEW_LINE);
        }

        Player player = connection.getPlayer();
        Room room = RoomService.getRoom(player.getZone(), player.getAtX(), player.getAtY());
        Vitals playerVitals = player.getVitals();
        Vitals combatantVitals = combatWith.getVitals();

        int damageRoll = player.getCombatDamageRoll();
        int combatantDamageRoll = combatWith.getCombatDamageRoll();

        if(damageRoll > 0 && player.getSkills().contains(new CriticalStrikeSkill()) && rand.nextInt((int)(10.0 / player.getDexterityModifier())) == 0) {
            damageRoll *= 2;
            connection.println("You critically strike!");
        }

        playerVitals.setHealth(playerVitals.getHealth() - combatantDamageRoll);
        combatantVitals.setHealth(combatantVitals.getHealth() - damageRoll);

        if(damageRoll > 0) {
            connection.println("You swing at " + combatWith.getName() + " dealing " + damageRoll + " damage.");
        } else {
            connection.println("You swing at " + combatWith.getName() + " and miss.");
        }

        if(player.getSkills().contains(new ParrySkill()) && rand.nextInt((int)(10.0 / player.getDexterityModifier())) == 0) {
            connection.println("You parry the enemy's attack!");
        } else {
            if(combatantDamageRoll > 0) {
                connection.println(combatWith.getName() + " swings at you dealing " + combatantDamageRoll + " damage.");
            } else {
                connection.println(combatWith.getName() + " swings at you and misses.");
            }
        }

        String combatantHealthString = getCombatantHealthString(combatWith);
        connection.println(combatantHealthString);
        connection.showPrompt();

        if(playerVitals.getHealth() <= 0) {
            System.out.println(player.getName() + " has been slain by " + combatWith.getName() + "!");
            connection.printSolo(Colors.NEW_LINE + "You have died.");
            room.printToOthers(player.getName() + " has been slain by " + combatWith.getName() + "!", connection);
            connection.getPlayer().maxVitals();
            int experienceLoss = combatWith.getExperienceGain();
            int tnl = connection.getPlayer().getToNextLevel();
            if(experienceLoss > tnl) {
                experienceLoss = tnl;
            }
            connection.getPlayer().getExperience().add(-experienceLoss); //todo: look at experience loss :)
            connection.changeState(new PlayingState(connection));
            new RecallCommand().execute(connection, new String[]{});
            PlayerService.save(player);
        } else if(combatantVitals.getHealth() <= 0) {
            room.removeMob((Mob) combatWith);
            connection.changeState(new PlayingState(connection));

            Collection<Item> inventory = combatWith.getInventory();
            Collection<Item> equipment = combatWith.getEquipment().values();

            connection.println(Colors.NEW_LINE + "You have slain " + combatWith.getName() + " in horrifying combat.");
            System.out.println(player.getName() + " has slain " + combatWith.getName() + ".");    
            room.printToOthers(player.getName() + " has slain " + combatWith.getName() + ".", connection);

            boolean autoloot = true;
            if(autoloot) {
                for(Item item : inventory) {
                    player.addItem(item);
                    connection.println("You get " + item.getName() + " from the corpse of " + combatWith.getName() + ".");
                }

                for(Item item : equipment) {
                    player.addItem(item);
                    connection.println("You get " + item.getName() + " from the corpse of " + combatWith.getName() + ".");
                }
            } else {
                for(Item item : inventory) {
                    room.addItem(item);
                    connection.println(item.getName() + " falls to the floor.");
                }

                for(Item item : equipment) {
                    room.addItem(item);
                    connection.println(item.getName() + " falls to the floor.");
                }
            }

            int experienceGain = combatWith.getExperienceGain();

            player.addExperience(experienceGain, connection);

            Quest quest = player.getQuest();
            if(quest != null && quest instanceof KillQuest && !quest.isComplete(player)) {
                KillQuest killQuest = (KillQuest) quest;
                if(killQuest.getMob().equals(combatWith)) {
                    connection.println("You have slain the target of your quest!" + Colors.NEW_LINE);
                    killQuest.setComplete(true);
                }
            }

            connection.showPrompt();
            PlayerService.save(player);
        }
    }

    public static PlayerCommand processInputBuffer(ServerConnection connection, List<String> inputBuffer) {
        Iterator<String> it = inputBuffer.iterator();
        if(!it.hasNext()) {
            return null;
        }

        String line = it.next();
        it.remove();

        String[] split = line.split(" ");
        String[] params = new String[split.length - 1];
        for(int i = 1; i < split.length; i++) {
            params[i-1] = split[i];
        }

        line = split[0];

        try {
            PlayerCommand skill = (Skill) TargetableService.findTarget(line, connection.getPlayer().getSkills());

            if(skill == null) {
                PlayerCommand spell = (PlayerCommand) TargetableService.findTarget(line, Arrays.asList((Targetable) new SpellCommand()));
                if(spell == null) {
                    whatThe(connection, line);
                    return processInputBuffer(connection, inputBuffer);
                } else {
                    spell.setParams(params);
                    return spell;
                }
            } else {
                skill.setParams(params);
                return skill;
            }

        } catch(Throwable t) {
            connection.printSolo("Don't do that anymore.");
            t.printStackTrace();
        }

        return null;
    }

    public static String getCombatantHealthString(MudCharacter combatant) {
        Vitals combatantVitals = combatant.getVitals();
        int health = combatantVitals.getHealth();
        int maxHealth = combatantVitals.getMaxHealth();

        int number = Math.round(10 * health / maxHealth);
        if(number < 0) number = -1;
        String looksLike = " " + combatScales.get(number);
        StringBuilder sb = new StringBuilder();
        sb.append(combatant.getName());
        sb.append(looksLike);
        sb.append(" <");
        for(int i = 0; i < 10; i++) {
            if(number <= i) {
                sb.append("-");
            } else {
                sb.append("*");
            }
        }
        sb.append(">");
        sb.append(Colors.NEW_LINE);
        return sb.toString();
    }
}
